RBT红黑树——数据结构系列教程(c++版)

梦想不会自己发光,真正闪耀的是那个为梦狂奔的你。献给知行的孩子们!(Eric.He著)


  红黑树是一种自平衡的二叉搜索树(BST),相比严格平衡的AVL树,红黑树的平衡调整代价更低,在插入/删除频繁的场景下性能更优。本教程基于链表实现红黑树,从核心原理、代码结构到实战使用,全面讲解红黑树的原理与实现,功能包含插入、删除、三种遍历、旋转、内存释放等核心功能。

教程目录导航

一、红黑树核心原理

1.1 红黑树的5条核心性质

红黑树的平衡依赖以下5条不可破坏的性质(NIL为虚拟叶子节点):

  1. 每个节点要么是红色,要么是黑色;
  2. 根节点必须是黑色;
  3. 所有NIL(虚拟叶子)节点都是黑色;
  4. 红色节点的子节点必须全为黑色(无连续红节点);
  5. 从任意节点到其所有叶子节点的路径上,黑色节点数量相同(黑高一致)。

演示动画

1.2 核心操作的设计思路

红黑树的操作分为“BST基础操作”+“平衡修复”两阶段:

1.3 存储结构

RBT红黑树结构是一对多的关系,除了树根之外,每一个节点有唯一的直接前驱(父亲),除了叶子之外,每一个节点有一个或两个直接后继(孩子)。

可以采用顺序存储和链式存储两种形式:

(1)顺序存储

每一个节点有二个域,即数据域data、color节点颜色,RBT通过旋转实现平衡,采用二维数组时间复杂度比较高,因此RBT树一般不采用顺序存储结构。

(1)链式存储

每一个节点有五个域,即数据域data、color节点颜色、parent节点地址、lchild左节点地址、rchild右节点地址。

二、代码结构解析

2.1 整体架构

代码分为3个核心部分:

模块 作用 关键结构/函数
颜色枚举 定义节点颜色 enum { RED, BLACK }
节点结构体 存储数据、颜色、父子指针 RBTNode<T>(模板泛型)
红黑树结构体 封装所有操作 LinkedRBTree<T>(模板类)

2.2 关键结构体详解

(1)颜色枚举

typedef enum { RED, BLACK } Color;

极简的枚举类型,仅定义红/黑两种颜色,是红黑树的基础标识。

(2)红黑树节点(RBTNode)

template <typename T>
struct RBTNode {
    T data;               // 存储任意类型数据(模板泛型)
    Color color;          // 节点颜色
    RBTNode<T>* left;     // 左子节点
    RBTNode<T>* right;    // 右子节点
    RBTNode<T>* parent;   // 父节点(链表实现核心,便于向上调整)

    // 构造函数:默认红色(插入优化)
    RBTNode(const T& val, Color c = Color::RED) : data(val), color(c), left(nullptr), right(nullptr), parent(nullptr) {}
};

(3)红黑树(LinkedRBTree)

分为私有成员(内部实现)和公有成员(对外接口):

template <typename T>
struct LinkedRBTree{
private:
    // 私有成员变量声明
    RBTNode<T>* root;  // 根节点
    RBTNode<T>* NIL;   // 虚拟叶子节点(统一空指针处理)
    // 私有成员函数声明
    int getHeight(RBTNode* node);                    //获取树的高度
    RBTNode* createNode(const T& val);               //创建新节点
    RBTNode* findMin(RBTNode* node);              // 找最小值节点(后继节点)
    void rightRotate(RBTNode* x);                    // 右旋转
    void leftRotate(RBTNode* x);                     // 左旋转
    void transplant(RBTNode* u, RBTNode* v);      // 节点替换
    void insertFixup(RBTNode* z);                    // 插入后修复红黑树性质
    void deleteFixup(RBTNode* x);                    // 删除后修复红黑树性质
    bool searchNode(RBTNode* node, const T& val);    // 查找节点(模板类型)
    void preOrder(RBTNode* node);                    // 前序遍历(根 → 左 → 右)
    void inOrder(RBTNode* node);                     // 中序遍历(左 → 根 → 右)→ 升序输出
    void postOrder(RBTNode* node);                   // 后序遍历(左 → 右 → 根)
    void destroyTree(RBTNode* node);                 // 销毁整棵树(防止内存泄漏)
public:    
    // 构造函数
    LinkedRBTree() : root(nullptr), level(0), depth(0), qty(0) {
        NIL = new RBTNode(T{}, Color::BLACK);
        root = NIL;
    }
    // 公共成员函数声明
    int isEmpty();                   // 判断树是否为空
    int isFull();                   // 判断树是否已满(链表实现永远不满)
    int getHeight();                //获取树调试
    void insertNode(const T& val);  // 插入节点
    void deleteNode(const T& val);  // 删除节点
    bool searchNode(const T& val);  // 查找节点
    void preOrder();                // 前序遍历(对外接口)
    void inOrder();                 // 中序遍历(对外接口)
    void postOrder();               // 后序遍历(对外接口)
    void destroyTree();             // 销毁整棵树(防止内存泄漏)
    // 析构函数:自动销毁树,避免内存泄漏
    ~LinkedRBTree() {
        destroyTree();
        delete NIL;
    }
};
注意: NIL节点统一处理空指针(避免频繁判断nullptr),所有叶子节点指向NIL。

三、核心操作实现详解

3.1 旋转操作(平衡调整的基础)

旋转是红黑树调整的核心,分为左/右旋转,原理是“节点位置互换,保持BST的节点顺序”。

(1)左旋转(leftRotate)

与右旋转对称,将节点x的右子节点y提升为父节点,x成为y的左子:

template <typename T>
void LinkedRBTree<T>::leftRotate(RBTNode<T>* x) {
    RBTNode<T>* y = x->right;  // y是x的右子
    x->right = y->left;        // x的右子替换为y的左子

    if (y->left != NIL) {
        y->left->parent = x;
    }

    y->parent = x->parent;  // y继承x的父节点
    if (x->parent == NIL) { // x是根节点
        root = y;
    } else if (x == x->parent->left) {  // x是父节点的左子
        x->parent->left = y;
    } else {    // x是父节点的右子
        x->parent->right = y;
    }

    y->left = x;    // x成为y的左子
    x->parent = y;
}

(2)右旋转(rightRotate)

将节点x的左子节点y提升为父节点,x成为y的右子:

template <typename T>
void LinkedRBTree<T>::rightRotate(RBTNode<T>* x) {
    RBTNode<T>* y = x->left;  // y是x的左子(旋转核心)
    x->left = y->right;       // x的左子替换为y的右子

    // 处理y的右子父指针
    if (y->right != NIL) {
        y->right->parent = x;
    }

    // 继承父节点关系
    y->parent = x->parent;
    if (x->parent == NIL) {   // x是根节点
        root = y;
    } else if (x == x->parent->right) { // x是父节点的右子
        x->parent->right = y;
    } else { // x是父节点的左子
        x->parent->left = y;
    }

    // 最终调整:x成为y的右子
    y->right = x;
    x->parent = y;
}

3.2 插入操作(insertNode)

插入分为“BST插入”和“平衡修复”两步,核心是insertFixup

(1)BST插入阶段

新节点默认着色为红色(关键优化:红色不破坏“黑高一致性”,仅可能破坏“无连续红节点”,修复场景更少);

迭代查找插入位置,避免递归栈溢出,同时检查重复值:

template <typename T>
void LinkedRBTree<T>::insertNode(const T& val) {
    // 1. 迭代查找插入位置(避免递归)
    RBTNode<T>* curr = root;
    RBTNode<T>* parent = NIL;
    while (curr != NIL) {
        parent = curr;
        if (val == curr->data) { // 重复值检查
            std::cout << "Warning: " << val << " already exists!" << std::endl;
            return;
        } else if (val < curr->data) {
            curr = curr->left;
        } else {
            curr = curr->right;
        }
    }

    // 2. 创建新节点(默认红色)
    RBTNode<T>* z = createNode(val);
    z->parent = parent;

    // 3. 插入到树中
    if (parent == NIL) { // 空树,新节点为根
        root = z;
        root->color = BLACK; // 根节点强制黑色
        return;
    } else if (val < parent->data) {
        parent->left = z;
    } else {
        parent->right = z;
    }

    // 4. 修复红黑树性质
    insertFixup(z);
}
创建新节点(辅助函数)
template <typename T>
RBTNode<T>* LinkedRBTree<T>::createNode(const T& val) {
    RBTNode<T>* node = new RBTNode<T>(val);
    node->left = NIL;
    node->right = NIL;
    node->parent = NIL;
    node->color = RED;
    qty++;
    return node;
}

(2)插入修复(insertFixup)

新节点(记为z)插入后,仅可能破坏性质4(连续红节点),需根据z的父节点(p)、祖父节点(g)、叔父节点(u)的颜色/位置分场景修复:

场景 特征 修复方式
场景0 z是根节点 直接将z设为黑色
场景1 父节点p是黑色 无需操作(所有性质满足)
场景2 父节点p是红色(需修复) 分以下子场景
子场景2.1 叔父u是红色 重着色(p黑、u黑、g红)+ 递归向上
子场景2.2 叔父u是黑色 旋转+重着色
子场景2.2.1 z-p-g成直线(左左/右右) 一次旋转+颜色交换
子场景2.2.2 z-p-g成折线(左右/右左) 先旋转转直线,再按2.2.1处理

注:“左左”指p是g的左子,z是p的左子;“左右”指p是g的左子,z是p的右子(右右/右左对称)。

template <typename T>
void LinkedRBTree<T>::insertFixup(RBTNode<T>* z) {
    // 循环修复:直到父节点为黑色或z成为根
    while (z->parent->color == Color::RED ) {
        if (z->parent == z->parent->parent->left) { // 父节点是祖父的左子
            RBTNode<T>* u = z->parent->parent->right;     // 叔父节点(祖父的右子)

            // 子场景2.1:叔父是红色 → 重着色+递归向上
            if (u->color == Color::RED) {
                z->parent->color = Color::BLACK;    // 父节点改黑
                u->color = Color::BLACK;            // 叔父改黑
                z->parent->parent->color = Color::RED; // 祖父改红
                z = z->parent->parent;              // 递归处理祖父
            } else { // 子场景2.2:叔父是黑色
                // 子场景2.2.2:折线(左右)→ 先左旋转直线
                if (z == z->parent->right) {
                    z = z->parent;
                    leftRotate(z);
                }
                // 子场景2.2.1:直线(左左)→ 右旋+颜色交换
                z->parent->color = Color::BLACK;
                z->parent->parent->color = Color::RED;
                rightRotate(z->parent->parent);
            }
        } else { // 父节点是祖父的右子(对称逻辑)
            RBTNode<T>* u = z->parent->parent->left;

            if (u->color == Color::RED) {
                z->parent->color = Color::BLACK;
                u->color = Color::BLACK;
                z->parent->parent->color = Color::RED;
                z = z->parent->parent;
            } else {
                // 子场景2.2.2:折线(右左)→ 先右旋转直线
                if (z == z->parent->left) {
                    z = z->parent;
                    rightRotate(z);
                }
                // 子场景2.2.1:直线(右右)→ 左旋+颜色交换
                z->parent->color = Color::BLACK;
                z->parent->parent->color = Color::RED;
                leftRotate(z->parent->parent);
            }
        }
    }
    // 强制根节点为黑色(修复场景0)
    root->color = Color::BLACK;
}

3.3 删除操作(deleteNode)

删除是红黑树最复杂的操作,分为“BST删除”和“平衡修复”两步。

(1)BST删除阶段

红黑树删除节点的逻辑与 BST 一致,但需区分 3 种情况:

  1. 情况 一:待删节点z无左子 → 用y(z的右子)替代z
  2. 情况 二:待删节点z无右子 → 用y(z的左子)替代z;
  3. 情况 三:待删节点z有左右子 → 找到z的后继节点y(右子树最小值节点),用y替代z、用x替代y(x->y->z)(z从树中分离),y继承z的颜色(y的颜色丢失)。

注:待删节点z的子不包括“红黑树”中的叶子结点(NIL)。

template <typename T>
void LinkedRBTree<T>::deleteNode(const T& val) {
    // 1. 查找待删除节点z
    RBTNode<T>* z = root;
    while (z != NIL && z->data != val) {
        if (val < z->data)
            z = z->left;
        else
            z = z->right;
    }
    if (z == NIL) {
        std::cout << "节点" << val << "不存在!" << std::endl;
        return;
    }

    // 2. 替换待删除节点z
    RBTNode<T>* y = nullptr;   //z的代替者
    RBTNode<T>* x = nullptr;   //y的代替者
    Color yOriginalColor;       //y原来的颜色

    // 情况1:z无左子,用右子替代z的位置
    if (z->left == NIL) {
        yOriginalColor = z->right->color;  // 记录y的原始颜色
        x = z->right;                      // 记录y的原始位置
        z->right->color = z->color;        // 将y的颜色设置成z的颜色(y的颜色丢失)。
        transplant(z, z->right);           // 将z->right转移至z的位置,让z从树中分离
    }
    // 情况2:z无右子,用左子替代z的位置
    else if (z->right == NIL) {
        yOriginalColor = z->left->color;    // 记录y的原始颜色
        x = z->left;                        // 记录y的原始位置
        z->left->color = z->color;          // 将y的颜色设置成z的颜色(y的颜色丢失)。
        transplant(z, z->left);             //将z->left转移至z的位置,让z从树中分离
    }
    // 情况3:z有左右子 → 找到z的后继节点y(右子树最小值节点或者左子树最大值节点),用y替代z,用x替代y(x->y->z)。
    else {
        // a. 找后继
        y = findMin(z->right);      // 找后继,右子树最小值节点
        //y = findMax(z->left);     // 找后继,左子树最大值节点
        yOriginalColor = y->color;  // 记录y的原始颜色
        x = y->right;               // 记录y的原始位置

        // b. 将y->right转移至y的位置,让y从树中分离
        transplant(y, y->right);

        // c. 将z的右子树转移至y的右子树
        y->right = z->right;        //将z->right转移至y->right
        y->right->parent = y;       //将z->right父亲指针指向y,至此z的右子成功转移至y的右子树

        // d. 将y移动至z的位置,让z从树中分离
        transplant(z, y);

        //e. 将z的左子树转转移到y的左子树
        y->left = z->left;      //将z->left转移至y->left
        y->left->parent = y;    //将z->left父亲指针指向y,至此z的左子成功转移至y的左子
        y->color = z->color;    //将y的颜色设置成z的颜色(y的颜色丢失),至此y成功替换掉z。
    }

    //替换后,y的颜色丢失,若丢失是红色,则不做任何操作,红黑树的任何属性都不会被破坏;
    //若丢失是黑色的,显然它所在的路径上就少一个黑色结点,红黑树的性质就被破坏了,这时执行一个 deleteFixup(x)从y的替代者y->right(x)处开始修补这棵树。
    if (yOriginalColor == Color::BLACK) {

        deleteFixup(x);
    }

    // 3. 删除节点z
    delete z;
}

节点替换(transplant)辅助函数

节点替换是红黑树删除操作的核心辅助函数,作用是将节点u的位置替换为节点v,同时正确维护父节点和子节点的指针关系。该函数不处理v的左右子节点,仅关注“位置替换”的核心逻辑。

template <typename T>
    void LinkedRBTree<T>::transplant(RBTNode<T>* u, RBTNode<T>* v) {
        if (u->parent == NIL) {
            // 场景1:u是根节点 → v成为新根
            root = v;
        } else if (u == u->parent->left) {
            // 场景2:u是父节点的左子 → v替换为左子
            u->parent->left = v;
        } else {
            // 场景3:u是父节点的右子 → v替换为右子
            u->parent->right = v;
        }
        // 关键:维护v的父指针(无论v是否为NIL)
        v->parent = u->parent;
    }
transplant在删除中的作用总结:
1. 情况1/2:直接调用transplant替换待删节点z的位置;
2. 情况3:分两次调用transplant——先删除后继节点y的原位置,再用y替换z的位置;
3. 所有替换操作均通过transplant保证父子指针的正确性,避免手动修改指针导致的错误。

找最小值节点(后继节点)辅助函数

template <typename T>
    RBTNode<T>* LinkedRBTree<T>::findMin(RBTNode<T>* node) {
        if (node == NIL) return NIL;
        while (node->left != NIL) {
            node = node->left;
        }
        return node;
    }

(2)删除修复

y替代z后,y的颜色丢失,若丢失是红色,则不做任何操作,红黑树的任何属性都不会被破坏;

若丢失是黑色的,显然它所在的路径上就少一个黑色结点,红黑树的性质就被破坏了,这时执行 deleteFixup 修复树。

修复方法需根据 x(原y的右子节点)和 w(x的兄弟节点),分场景修复。

场景拆解表:
场景 特征(x为左子) 核心操作 目标
场景1 w是红色 w改黑、左旋父节点(p)、父改红 将w转为黑色,进入后续场景
场景2 w黑,w的两个子(w1、w2)均黑 w改红,x上移为父节点 向上传递黑高失衡问题
场景3 w黑,w左子(w1)红、右子(w2)黑 w左子(w1)改黑、w改红 + 右旋w 将折线转为直线,进入场景4
场景4 w黑,w右子红 左旋父节点、p和w的颜色对调、w2改黑 修复黑高,终止循环
终止条件 x是根 或 x变为红色 x强制设为黑色 保证根为黑,修复完成
template <typename T>
void LinkedRBTree<T>::deleteFixup(RBTNode<T>* x) {
// 循环修复:直到x是根 或 x变为红色(红节点不影响黑高)
while (x != root && x->color == Color::BLACK) {
if (x == x->parent->left) {  // x是左子(主场景)
    RBTNode<T>* w = x->parent->right;  // w是x的兄弟节点

    // ========== 场景1:w是红色 ==========
    // 特征:w红 → 父节点必黑(无连续红节点)
    // 目标:将w转为黑色,转为场景3/4/5
    if (w->color == Color::RED) {
        w->color = Color::BLACK;       // w改黑
        x->parent->color = Color::RED; // 父节点改红
        leftRotate(x->parent);         // 左旋父节点
        w = x->parent->right;          // 更新w为新的兄弟节点
    }

    // ========== 场景2:w是黑色,且w的两个子均为黑 ==========
    // 特征:无红节点可借,黑高仍失衡
    // 目标:将w改红,x上移为父节点,继续修复
    if (w->left->color == Color::BLACK && w->right->color == Color::BLACK) {
        w->color = Color::RED;         // w改红(补充黑高)
        x = x->parent;                 // x上移,继续修复父节点
    } else {
        // ========== 场景3:w是黑色,w左子红、右子黑 ==========
        // 特征:折线型红节点,无法直接借
        // 目标:旋转转为直线型(场景4)
        if (w->right->color == Color::BLACK) {
            w->left->color = Color::BLACK;  // w左子改黑
            w->color = Color::RED;          // w改红
            rightRotate(w);                 // 右旋w,转直线
            w = x->parent->right;           // 更新w
        }

        // ========== 场景4:w是黑色,w右子红 ==========
        // 特征:直线型红节点,可借红节点补黑高
        // 目标:重着色+旋转,终止修复
        w->color = x->parent->color;     // w继承父节点颜色
        x->parent->color = Color::BLACK; // 父节点改黑(补黑高)
        w->right->color = Color::BLACK;  // w右子改黑(消除连续红)
        leftRotate(x->parent);           // 左旋父节点,平衡树结构
        x = root;                        // 终止循环(修复完成)
    }
} else {  // x是右子(对称逻辑)
    RBTNode<T>* w = x->parent->left;   // w是x的兄弟节点

    // 场景1:w是红色(对称)
    if (w->color == Color::RED) {
        w->color = Color::BLACK;
        x->parent->color = Color::RED;
        rightRotate(x->parent);
        w = x->parent->left;
    }

    // 场景2:w是黑色,且w的两个子均为黑(对称)
    if (w->right->color == Color::BLACK && w->left->color == Color::BLACK) {
        w->color = Color::RED;
        x = x->parent;
    } else {
        // 场景3:w是黑色,w右子红、左子黑(对称)
        if (w->left->color == Color::BLACK) {
            w->right->color = Color::BLACK;
            w->color = Color::RED;
            leftRotate(w);
            w = x->parent->left;
        }

        // 场景4:w是黑色,w左子红(对称)
        w->color = x->parent->color;
        x->parent->color = Color::BLACK;
        w->left->color = Color::BLACK;
        rightRotate(x->parent);
        x = root;
    }
}
}
// 最终修复:若x是根 或 x变为红色,强制设为黑色(保证根为黑)
x->color = Color::BLACK;
}
deleteFixup核心要点:
1. 修复仅处理x为黑色的情况(红节点不影响黑高);
2. 所有场景最终都会转为场景4(直线型红节点)或x成为根,终止修复;
3. 对称逻辑仅需反转旋转方向和左右子判断,核心思想一致;
4. 最终必须将x设为黑色,保证根节点为黑的性质。

3.4 遍历操作

红黑树遍历继承BST的遍历特性:

// 前序遍历(根 → 左 → 右)
template <typename T>
void LinkedRBTree<T>::inOrder(RBTNode<T>* node) {
    if (node == NIL) return;
    std::cout << "Data: " << node->data << ", Color: " << (node->color == RED ? "RED" : "BLACK") << ", My : " << node << ", Parent : " << node->parent << std::endl;
    preOrder(node->left);
    preOrder(node->right);
}
// 中序遍历(左→根→右)
template <typename T>
void LinkedRBTree<T>::inOrder(RBTNode<T>* node) {
    if (node == NIL) return;
    inOrder(node->left);
    std::cout << "Data: " << node->data << ", Color: " << (node->color == RED ? "RED" : "BLACK") << std::endl;
    inOrder(node->right);
}
// 后序遍历(左 → 右 → 根)
    template <typename T>
    void LinkedRBTree<T>::inOrder(RBTNode<T>* node) {
        if (node == NIL) return;
        postOrder(node->left);
        postOrder(node->right);
        std::cout << "Data: " << node->data << ", Color: " << (node->color == RED ? "RED" : "BLACK") << ", My : " << node << ", Parent : " << node->parent << std::endl;
    }

四、实战使用示例

4.1 基础类型(int)使用示例

#include "LinkedRBTree.h"
#include <iostream>

int main() {
    // 1. 创建红黑树对象
    LinkedRBTree<int> rbt;

    // 2. 插入节点
    rbt.insertNode(10);
    rbt.insertNode(20);
    rbt.insertNode(5);
    rbt.insertNode(30);

    // 3. 中序遍历(升序输出)
    std::cout << "中序遍历结果:" << std::endl;
    rbt.inOrder();

    // 4. 查找节点
    if (rbt.searchNode(20)) {
        std::cout << "找到节点20" << std::endl;
    }

    // 5. 删除节点
    rbt.deleteNode(10);
    std::cout << "删除10后中序遍历:" << std::endl;
    rbt.inOrder();

    // 6. 销毁树
    rbt.destroyTree();

    return 0;
}

4.2 自定义类型(Student)使用示例

需先定义Student类,并重载比较运算符(<==):

// Student类定义(Entitys.h)
#include <iostream>
#include "Entitys.h"
#include "LinkedRBTree.h"

// 使用示例
int main() {
    LinkedRBTree<Student> studentRbt;

    // 插入学生节点
    studentRbt.insertNode({1001,"Eric", "2012-05-18", "男", "汉", "湖南省湘潭县石潭镇", 400, "石潭傎芙蓉中学", "36","在读"});
    studentRbt.insertNode({1002,"Bob", "2012-05-18", "男", "汉", "湖南省湘潭县石潭镇", 400, "石潭傎芙蓉中学", "36","在读"});
    studentRbt.insertNode({1003,"Charlie", "2012-05-18", "男", "汉", "湖南省湘潭县石潭镇", 400, "石潭傎芙蓉中学", "36","在读"});

    // 中序遍历(按ID升序)
    studentRbt.inOrder();

    // 删除学生
    studentRbt.deleteNode({1003,"", "", "", "", "", 0, "", "",""}); // 仅需ID匹配

    return 0;
}

五、完整可运行代码

5.1 Entitys.h 头文件


#ifndef ENTITYS_H_INCLUDED
#define ENTITYS_H_INCLUDED

//************************************************************************************************************************************************************************
//                                                                      自定义类型
//************************************************************************************************************************************************************************

//========================================================================================================================================================================
//                                                                    学生结构体(Student)
//========================================================================================================================================================================
struct Student {
    int id;// 学号
    std::string name;// 姓名
    std::string dob;// 出生日期
    std::string sex;// 性别
    std::string gender;// 民族
    std::string address;// 地址
    float score;// 入学总分
    std::string school;// 学校
    std::string team;// 班级
    std::string status;// 状态

    bool operator<(const Student& other) const { return id < other.id; }
    bool operator>(const Student& other) const { return id > other.id; }
    bool operator==(const Student& other) const { return id == other.id; }
    bool operator!=(const Student& other) const { return id != other.id; }

    friend std::ostream& operator<<(std::ostream& os, const Student& s) {
        os << "[" << s.id<< ", " << s.name << ", " << s.dob << ", " << s.sex << ", " << s.gender  << ", " << s.address << ", " << s.score<< ", " << s.school<< ", " << s.team<< ", " << s.status << "]";
        return os;
    }
};

//========================================================================================================================================================================
//
//                                                                    学生索引结构体(Student)
//
//========================================================================================================================================================================
struct StudentIndex {
    int id;// 学号
    int row;// 行号

    bool operator<(const StudentIndex& other) const { return id < other.id; }
    bool operator>(const StudentIndex& other) const { return id > other.id; }
    bool operator==(const StudentIndex& other) const { return id == other.id; }
    bool operator!=(const StudentIndex& other) const { return id != other.id; }

    friend std::ostream& operator<<(std::ostream& os, const StudentIndex& i) {
        os << "[" << i.id << ", " << i.row<< "]";
        return os;
    }
};

//========================================================================================================================================================================
//                                                                    迷宫坐标结构体(Pos)
//========================================================================================================================================================================
struct Pos{
    int x;     //x坐标
    int y;    //y坐标
    int step; //步数
};

//========================================================================================================================================================================
//                                                                    打印任务结构体(PrintTask)
//========================================================================================================================================================================
struct PrintTask{
    int taskId;     // 任务ID
    char content[50]; // 打印内容
};

#endif // ENTITYS_H_INCLUDED
        

5.2 LinkedRBTree.h 头文件


#ifndef LINKEDRBTREE_H_INCLUDED
#define LINKEDRBTREE_H_INCLUDED

#include <iostream>
#include <string>
#include "Entitys.h"

// 颜色枚举
typedef enum { RED, BLACK } Color;

// 红黑树节点结构体
template <typename T>
struct RBTNode {
    T data;
    Color color;
    RBTNode* left;
    RBTNode* right;
    RBTNode* parent;

    // 构造函数
    RBTNode(const T& val, Color c = RED) 
        : data(val), color(c), left(nullptr), right(nullptr), parent(nullptr) {}
};

// 红黑树结构体
template <typename T>
struct LinkedRBTree {
private:
    RBTNode<T>* root;
    RBTNode<T>* NIL;  // 虚拟叶子节点

    // 私有辅助函数
    RBTNode<T>* createNode(const T& val);                 // 创建新节点
    RBTNode<T>* findMin(RBTNode<T>* node);          // 找最小值节点(后继节点)
    RBTNode<T>* findMax(RBTNode<T>* node);          // 找最大值节点(后继节点)
    void leftRotate(RBTNode<T>* x);                       // 右旋转
    void rightRotate(RBTNode<T>* x);                      // 左旋转
    void transplant(RBTNode<T>* u, RBTNode<T>* v);  // 节点替换
    void insertFixup(RBTNode<T>* z);                      // 插入后修复红黑树性质
    void deleteFixup(RBTNode<T>* x);                      // 删除后修复红黑树性质   
    bool searchNode(RBTNode<T>* node, const T& val);      // 查找节点(模板类型)
    void preOrder(RBTNode<T>* node);                      // 前序遍历(根 → 左 → 右)
    void inOrder(RBTNode<T>* node);                       // 中序遍历(左 → 根 → 右)→ 升序输出
    void postOrder(RBTNode<T>* node);                     // 后序遍历(左 → 右 → 根)
    void destroyTree(RBTNode<T>* node);                   // 销毁整棵树(防止内存泄漏)

public:
    // 构造函数
    LinkedRBTree() : root(nullptr) {
        NIL = new RBTNode<T>(T{}, Color::BLACK);
        root = NIL;
    }

    // 对外接口
    int isEmpty();                    // 判断树是否为空
    void insertNode(const T& val);  // 插入节点
    void deleteNode(const T& val);  // 删除节点
    bool searchNode(const T& val);  // 查找节点
    void preOrder();                // 前序遍历
    void inOrder();                 // 中序遍历
    void postOrder();               // 后序遍历
    void destroyTree();

    // 析构函数:自动销毁树,避免内存泄漏
    ~LinkedRBTree() {
        destroyTree();
        delete NIL;
    }
};

#endif // LINKEDRBTREE_H_INCLUDED

5.3 LinkedRBTree.cpp 程序代码


#include "LinkedRBTree.h"
//=================================================================================================
// 私有函数实现
//=================================================================================================

//创建新节点
template <typename T>
RBTNode<T>* LinkedRBTree<T>::createNode(const T& val) {
    RBTNode<T>* node = new RBTNode<T>(val);
    node->left = NIL;
    node->right = NIL;
    node->parent = NIL;
    node->color = RED;
    return node;
}

// 找最小值节点(后继节点)
template <typename T>
RBTNode<T>* LinkedRBTree<T>::findMin(RBTNode<T>* node) {
    if (node == NIL) return NIL;
    while (node->left != NIL) {
        node = node->left;
    }
    return node;
}

// 找最大值节点(后继节点)
template <typename T>
RBTNode<T>* LinkedRBTree<T>::findMax(RBTNode<T>* node) {
    if (node == NIL) return NIL;
    while (node->right != NIL) {
        node = node->right;
    }
    return node;
}

// 左旋
template <typename T>
void LinkedRBTree<T>::leftRotate(RBTNode<T>* x) {
    RBTNode<T>* y = x->right;
    x->right = y->left;

    if (y->left != NIL) {
        y->left->parent = x;
    }

    y->parent = x->parent;
    if (x->parent == NIL) {
        root = y;
    } else if (x == x->parent->left) {
        x->parent->left = y;
    } else {
        x->parent->right = y;
    }

    y->left = x;
    x->parent = y;
}

// 右旋
template <typename T>
void LinkedRBTree<T>::rightRotate(RBTNode<T>* x) {
    RBTNode<T>* y = x->left;
    x->left = y->right;

    if (y->right != NIL) {
        y->right->parent = x;
    }

    y->parent = x->parent;
    if (x->parent == NIL) {
        root = y;
    } else if (x == x->parent->right) {
        x->parent->right = y;
    } else {
        x->parent->left = y;
    }

    y->right = x;
    x->parent = y;
}

// 节点替换
template <typename T>
void LinkedRBTree<T>::transplant(RBTNode<T>* u, RBTNode<T>* v) {
    if (u->parent == NIL) {
        root = v;
    } else if (u == u->parent->left) {
        u->parent->left = v;
    } else {
        u->parent->right = v;
    }
    v->parent = u->parent;
}

// 插入修复
template <typename T>
void LinkedRBTree<T>::insertFixup(RBTNode<T>* z) {
    // 循环修复:直到父节点为黑色或z成为根
    while (z->parent->color == Color::RED ) {
        if (z->parent == z->parent->parent->left) { // 父节点是祖父的左子
            RBTNode<T>* u = z->parent->parent->right;     // 叔父节点(祖父的右子)

            // 子场景2.1:叔父是红色 → 重着色+递归向上
            if (u->color == Color::RED) {
                z->parent->color = Color::BLACK;    // 父节点改黑
                u->color = Color::BLACK;            // 叔父改黑
                z->parent->parent->color = Color::RED; // 祖父改红
                z = z->parent->parent;              // 递归处理祖父
            } else { // 子场景2.2:叔父是黑色
                // 子场景2.2.2:折线(左右)→ 先左旋转直线
                if (z == z->parent->right) {
                    z = z->parent;
                    leftRotate(z);
                }
                // 子场景2.2.1:直线(左左)→ 右旋+颜色交换
                z->parent->color = Color::BLACK;
                z->parent->parent->color = Color::RED;
                rightRotate(z->parent->parent);
            }
        } else { // 父节点是祖父的右子(对称逻辑)
            RBTNode<T>* u = z->parent->parent->left;

            if (u->color == Color::RED) {
                z->parent->color = Color::BLACK;
                u->color = Color::BLACK;
                z->parent->parent->color = Color::RED;
                z = z->parent->parent;
            } else {
                // 子场景2.2.2:折线(右左)→ 先右旋转直线
                if (z == z->parent->left) {
                    z = z->parent;
                    rightRotate(z);
                }
                // 子场景2.2.1:直线(右右)→ 左旋+颜色交换
                z->parent->color = Color::BLACK;
                z->parent->parent->color = Color::RED;
                leftRotate(z->parent->parent);
            }
        }
    }
    // 强制根节点为黑色(修复场景0)
    root->color = Color::BLACK;
}

// 删除修复
template <typename T>
void LinkedRBTree<T>::deleteFixup(RBTNode<T>* x) {
    // 循环修复:直到x是根 或 x变为红色(红节点不影响黑高)
    while (x != root && x->color == Color::BLACK) {
        if (x == x->parent->left) {  // x是左子(主场景)
            RBTNode<T>* w = x->parent->right;   // w是x的兄弟节点

            // ========== 场景1:w是红色 ==========
            // 特征:w红 → 父节点必黑(无连续红节点)
            // 操作:w改黑、父改红 + 左旋父节点
            // 目标:将w转为黑色,转为场景3/4/5
            if (w->color == Color::RED) {
                w->color = Color::BLACK;
                x->parent->color = Color::RED;
                leftRotate(x->parent);
                w = x->parent->right;
            }

            // ========== 场景2:w黑,w的两个子均黑 ==========
            // 特征:无红节点可借,黑高仍失衡
            // 操作:w改红,x上移为父节点
            // 目标:向上传递黑高失衡问题
            if (w->left->color == Color::BLACK && w->right->color == Color::BLACK) {
                w->color = Color::RED;
                x = x->parent;
            } else {
                // ========== 场景3:w是黑色,w左子红、右子黑 ==========
                // 特征:折线型红节点,无法直接借
                // 操作:w左子改黑、w改红 + 右旋w
                // 目标:旋转转为直线型(场景4)
                if (w->right->color == Color::BLACK) {
                    w->left->color = Color::BLACK;
                    w->color = Color::RED;
                    rightRotate(w);
                    w = x->parent->right;
                }
                // ========== 场景4:w是黑色,w右子红 ==========
                // 特征:直线型红节点,可借红节点补黑高
                // 操作:w继承父颜色、父改黑、w右子改黑 + 左旋父节点
                // 目标:重着色+旋转,终止修复
                w->color = x->parent->color;
                x->parent->color = Color::BLACK;
                w->right->color = Color::BLACK;
                leftRotate(x->parent);
                x = root;  // 终止循环
            }
        } else {  // x是右子(对称逻辑)
            RBTNode<T>* w = x->parent->left;

            if (w->color == Color::RED) {
                w->color = Color::BLACK;
                x->parent->color = Color::RED;
                rightRotate(x->parent);
                w = x->parent->left;
            }

            if (w->right->color == Color::BLACK && w->left->color == Color::BLACK) {
                w->color = Color::RED;
                x = x->parent;
            } else {
                if (w->left->color == Color::BLACK) {
                    w->right->color = Color::BLACK;
                    w->color = Color::RED;
                    leftRotate(w);
                    w = x->parent->left;
                }
                w->color = x->parent->color;
                x->parent->color = Color::BLACK;
                w->left->color = Color::BLACK;
                rightRotate(x->parent);
                x = root;
            }
        }
    }
    // 最终修复:若x是根 或 x变为红色,强制设为黑色(保证根为黑)
    x->color = Color::BLACK;
}

// 查找节点(模板类型)
template <typename T>
bool LinkedRBTree<T>::searchNode(RBTNode<T>* node, const T& val) {
    if (node == NIL) return false;
    if (val == node->data) return true;
    return val < node->data ? searchNode(node->left, val) : searchNode(node->right, val);
}

// 前序遍历(根 → 左 → 右)
template <typename T>
void LinkedRBTree<T>::preOrder(RBTNode<T>* node) {
    if (node == NIL) return;
    std::cout << "Data: " << node->data << ", Color: " << (node->color == RED ? "RED" : "BLACK") << ", My : " << node << ", Parent : " << node->parent << std::endl;
    preOrder(node->left);
    preOrder(node->right);
}

// 中序遍历(左 → 根 → 右)→ 升序输出
template <typename T>
void LinkedRBTree<T>::inOrder(RBTNode<T>* node) {
    if (node == NIL) return;
    inOrder(node->left);
    std::cout << "Data: " << node->data << ", Color: " << (node->color == RED ? "RED" : "BLACK") << ", My : " << node << ", Parent : " << node->parent << std::endl;
    inOrder(node->right);
}

// 后序遍历(左 → 右 → 根)
template <typename T>
void LinkedRBTree<T>::postOrder(RBTNode<T>* node) {
    if (node == NIL) return;
    postOrder(node->left);
    postOrder(node->right);
    std::cout << "Data: " << node->data << ", Color: " << (node->color == RED ? "RED" : "BLACK") << ", My : " << node << ", Parent : " << node->parent << std::endl;
}

// 销毁整棵树(防止内存泄漏)
template <typename T>
void LinkedRBTree<T>::destroyTree(RBTNode<T>* node) {
    if (node == NIL) return;
    destroyTree(node->left);
    destroyTree(node->right);
    delete node;
}

//=================================================================================================
// 公共函数实现
//=================================================================================================

// 判断树是否为空
template <typename T>
    int LinkedRBTree<T>::isEmpty() {
        return (root == NIL) ? 1 : 0;
}

// 迭代式插入节点
template <typename T>
void LinkedRBTree<T>::insertNode(const T& val) {
    // 1. 检查重复值(提前遍历)
    RBTNode<T>* curr = root;
    RBTNode<T>* parent = NIL;

    // 迭代查找插入位置
    while (curr != NIL) {
        parent = curr;
        if (val == curr->data) {
            std::cout << "Warning: " << val << " already exists!" << std::endl;
            return; // 重复值,直接返回
        } else if (val < curr->data) {
            curr = curr->left;
        } else {
            curr = curr->right;
        }
    }

    // 2. 创建新节点
    RBTNode<T>* z = createNode(val);
    z->parent = parent;

    // 3. 插入节点到树中
    if (parent == NIL) {
        root = z; // 空树,新节点作为根
        root->color = BLACK; // 根节点强制为黑
        return;
    } else if (val < parent->data) {
        parent->left = z;
    } else {
        parent->right = z;
    }

    // 插入修复前校验z的合法性
    if (z == NIL || z == nullptr) {
        return;
    }
    // 4. 修复红黑树性质
    insertFixup(z);
}

// 删除函数
template <typename T>
void LinkedRBTree<T>::deleteNode(const T& val) {
    // 1. 查找待删除节点z
    RBTNode<T>* z = root;
    while (z != NIL && z->data != val) {
        if (val < z->data)
            z = z->left;
        else
            z = z->right;
    }
    if (z == NIL) {
        std::cout << "节点" << val << "不存在!" << std::endl;
        return;
    }

    // 2. 替换待删除节点z
    RBTNode<T>* y = nullptr;   //z的代替者
    RBTNode<T>* x = nullptr;   //y的代替者
    Color yOriginalColor;       //y原来的颜色

    // 情况1:z无左子,用右子替代z的位置
    if (z->left == NIL) {
        yOriginalColor = z->right->color;  // 记录y的原始颜色
        x = z->right;                      // 记录y的原始位置
        z->right->color = z->color;        // 将y的颜色设置成z的颜色(y的颜色丢失)。
        transplant(z, z->right);           // 将z->right转移至z的位置,让z从树中分离
    }
    // 情况2:z无右子,用左子替代z的位置
    else if (z->right == NIL) {
        yOriginalColor = z->left->color;    // 记录y的原始颜色
        x = z->left;                        // 记录y的原始位置
        z->left->color = z->color;          // 将y的颜色设置成z的颜色(y的颜色丢失)。
        transplant(z, z->left);             //将z->left转移至z的位置,让z从树中分离
    }
    // 情况3:z有左右子 → 找到z的后继节点y(右子树最小值节点或者左子树最大值节点),用y替代z,用x替代y(x->y->z)。
    else {
        // a. 找后继
        y = findMin(z->right);      // 找后继,右子树最小值节点
        //y = findMax(z->left);     // 找后继,左子树最大值节点
        yOriginalColor = y->color;  // 记录y的原始颜色
        x = y->right;               // 记录y的原始位置

        // b. 将y->right转移至y的位置,让y从树中分离
        transplant(y, y->right);

        // c. 将z的右子树转移至y的右子树
        y->right = z->right;        //将z->right转移至y->right
        y->right->parent = y;       //将z->right父亲指针指向y,至此z的右子成功转移至y的右子树

        // d. 将y移动至z的位置,让z从树中分离
        transplant(z, y);

        //e. 将z的左子树转转移到y的左子树
        y->left = z->left;      //将z->left转移至y->left
        y->left->parent = y;    //将z->left父亲指针指向y,至此z的左子成功转移至y的左子
        y->color = z->color;    //将y的颜色设置成z的颜色(y的颜色丢失),至此y成功替换掉z。
    }

    //替换后,y的颜色丢失,若丢失是红色,则不做任何操作,红黑树的任何属性都不会被破坏;
    //若丢失是黑色的,显然它所在的路径上就少一个黑色结点,红黑树的性质就被破坏了,这时执行一个 deleteFixup(x)从y的替代者y->right(x)处开始修补这棵树。
    if (yOriginalColor == Color::BLACK) {

        deleteFixup(x);
    }

    // 3. 删除节点z
    delete z;
}

// 查找节点
template <typename T>
bool LinkedRBTree<T>::searchNode(const T& val) {
    return searchNode(root, val);
}

// 前序遍历(对外接口)
template <typename T>
void LinkedRBTree<T>::preOrder() {
    if (isEmpty()) {
        std::cout << "Tree empty!" << std::endl;
        return;
    }
    std::cout << "===== Pre-Order =====" << std::endl;
    preOrder(root);
    std::cout << "=====================" << std::endl;
}

// 中序遍历(对外接口)
template <typename T>
void LinkedRBTree<T>::inOrder() {
    if (isEmpty()) {
        std::cout << "Tree empty!" << std::endl;
        return;
    }
    std::cout << "===== In-Order =====" << std::endl;
    inOrder(root);
    std::cout << "====================" << std::endl;
}

// 后序遍历(对外接口)
template <typename T>
void LinkedRBTree<T>::postOrder() {
    if (isEmpty()) {
        std::cout << "Tree empty!" << std::endl;
        return;
    }
    std::cout << "===== Post-Order =====" << std::endl;
    postOrder(root);
    std::cout << "======================" << std::endl;
}

// 销毁整棵树(防止内存泄漏)
template <typename T>
void LinkedRBTree<T>::destroyTree() {
    destroyTree(root);
    root = NIL;
}

// 显式实例化
template class LinkedRBTree<int>;
template class LinkedRBTree<char>;
template class LinkedRBTree<float>;
template class LinkedBSTree<std::string>;
template class LinkedAVLTree<Student>; // 新增:显式实例化Student类型 template class LinkedAVLTree<StudentIndex>; // 新增:显式实例化StudentIndex类型

5.4 main.cpp 测试代码

#include "LinkedRBTree.h"
#include <iostream>
#include <string>
#include "LinkedRBTree.h"

int main() {
    // ==================== 测试1:int类型红黑树 ====================
    LinkedRBTree<int> intTree;
    int intKeys[] = {10, 20, 30, 15, 25, 5, 8};
    int n = sizeof(intKeys)/sizeof(intKeys[0]);

    // 插入int节点
    std::cout << "=== Insert int keys: ";
    for (int i = 0; i < n; i++) {
        std::cout << intKeys[i] << " ";
        intTree.insertNode(intKeys[i]);
    }
    std::cout << "===" << std::endl;

    intTree.preOrder();

    // 查找测试
    int target = 31;
    if (intTree.searchNode(target)) {
        std::cout << "\nFound " << target << " in int tree!" << std::endl;
    }

    // 删除测试
    int deleteKey = 5;
    std::cout << "\n=== Delete key: " << deleteKey << " ===" << std::endl;
    intTree.deleteNode(deleteKey);
    // 前序遍历
    intTree.preOrder();

    // 销毁树(析构函数自动调用,此处显式调用演示)
    intTree.destroyTree();
    
    return 0;
}

六、注意事项与优化建议

5.1 注意事项

  • 模板类型要求:自定义类型必须重载<(排序)和==(查找/删除)运算符;
  • 内存管理:析构函数会自动销毁树,但手动调用destroyTree可提前释放内存;
  • 重复值处理:插入时会检查重复值,重复值直接返回(可根据需求修改为覆盖);
  • NIL节点:所有空指针统一指向NIL,避免空指针异常。

5.2 优化建议

七、总结

红黑树的核心是“牺牲严格平衡,降低调整代价”,通过颜色规则和旋转操作,保证树的高度始终在O(logn)级别。本教程基于链表实现的模板化红黑树代码,从原理到实战,覆盖了红黑树的核心操作与使用场景。掌握红黑树不仅能理解“自平衡”的设计思想,也能为后续学习STL容器打下基础。

你可以基于本教程的代码,尝试扩展功能(如批量删除、树的复制、序列化等),进一步加深对红黑树的理解。


返回顶部